package com.thx.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;

/**
 * 用于从类路径下加载文件.
 *
 * @author 贾红磊
 */
public class Loader {

  private static final Logger logger = LoggerFactory.getLogger(Loader.class);

  private static final String TSTR =
      "Caught Exception while in Loader.getResource. This may be innocuous.";

  // We conservatively assume that we are running under Java 1.x
  private static boolean java1 = true;

  private static boolean ignoreTcl = false;

  static {
    String prop = getSystemProperty("java.version", null);

    if (prop != null) {
      int index = prop.indexOf('.');
      if (index != -1) {
        if (prop.charAt(index + 1) != '1') {
          java1 = false;
        }
      }
    }
    String ignoreTclProp = getSystemProperty("log4j.ignoreTcl", null);
    if (ignoreTclProp != null) {
      ignoreTcl = toBoolean(ignoreTclProp, true);
    }
  }

  /**
   * This method will search for <code>resource</code> in different places. The rearch order is as
   * follows:
   * <ol>
   * <li>Search for <code>resource</code> using the thread context class loader under Java2. If that
   * fails, search for <code>resource</code> using the class loader that loaded this class
   * (<code>Loader</code>). Under JDK 1.1, only the the class loader that loaded this class (
   * <code>Loader</code>) is used.
   * <li>Try one last time with
   * 
   * <pre>
   * ClassLoader.getSystemResource(resource)
   * </pre>
   * 
   * that is is using the system class loader in JDK 1.2 and virtual machine's built-in class loader
   * in JDK 1.1.
   * </ol>
   */
  public static URL getResource(String resource) {
    ClassLoader classLoader = null;
    URL url = null;

    try {
      if (!java1) {
        classLoader = getTcl();
        if (classLoader != null) {
          logger.debug(
              "Trying to find [" + resource + "] using context classloader " + classLoader + ".");
          url = classLoader.getResource(resource);
          if (url != null) {
            return url;
          }
        }
      }

      // We could not find resource. Ler us now try with the
      // classloader that loaded this class.
      classLoader = Loader.class.getClassLoader();
      if (classLoader != null) {
        logger.debug("Trying to find [" + resource + "] using " + classLoader + " class loader.");
        url = classLoader.getResource(resource);
        if (url != null) {
          return url;
        }
      }
    } catch (Throwable ex) {
      logger.warn(TSTR, ex);
    }

    // Last ditch attempt: get the resource from the class path. It
    // may be the case that clazz was loaded by the Extentsion class
    // loader which the parent of the system class loader. Hence the
    // code below.
    logger.debug("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
    return ClassLoader.getSystemResource(resource);
  }

  /**
   * Are we running under JDK 1.x?
   */
  public static boolean isJava1() {
    return java1;
  }

  /**
   * Get the Thread Context Loader which is a JDK 1.2 feature. If we are running under JDK 1.1 or
   * anything else goes wrong the method returns <code>null</code>.
   */
  private static ClassLoader getTcl() throws IllegalAccessException, InvocationTargetException {

    // Are we running on a JDK 1.2 or later system?
    Method method = null;
    try {
      java.lang.Class[] cls = null;
      method = Thread.class.getMethod("getContextClassLoader", cls);
    } catch (NoSuchMethodException ex) {
      // We are running on JDK 1.1
      return null;
    }
    java.lang.Object[] cls = null;
    return (ClassLoader) method.invoke(Thread.currentThread(), cls);
  }

  /**
   * If running under JDK 1.2 load the specified class using the <code>Thread</code>
   * <code>contextClassLoader</code> if that fails try Class.forname. Under JDK 1.1 only
   * Class.forName is used.
   */
  public static Class loadClass(String clazz) throws ClassNotFoundException {
    // Just call Class.forName(clazz) if we are running under JDK 1.1
    // or if we are instructed to ignore the TCL.
    if (java1 || ignoreTcl) {
      return Class.forName(clazz);
    } else {
      try {
        return getTcl().loadClass(clazz);
      } catch (Throwable ex) {
        // we reached here because tcl was null or because of a
        // security exception, or because clazz could not be loaded...
        // In any case we now try one more time
        return Class.forName(clazz);
      }
    }
  }

  /**
   * 读取系统属性.
   *
   * @param key 系统属性key
   * @param def 如果没有设置系统属性, 那么走默认值
   */
  public static String getSystemProperty(String key, String def) {
    try {
      return System.getProperty(key, def);
    } catch (Throwable ex) { // MS-Java throws
      logger.debug("Was not allowed to read system property \"" + key + "\".");
      return def;
    }
  }

  /**
   * If <code>value</code> is "true", then <code>true</code> is returned. If <code>value</code> is
   * "false", then <code>true</code> is returned. Otherwise, <code>default</code> is returned.
   *
   * <p/>
   * Case of value is unimportant.
   */
  private static boolean toBoolean(String value, boolean defaultVal) {
    if (value == null) {
      return defaultVal;
    }
    String trimmedVal = value.trim();
    if ("true".equalsIgnoreCase(trimmedVal)) {
      return true;
    }
    if ("false".equalsIgnoreCase(trimmedVal)) {
      return false;
    }
    return defaultVal;
  }

}
